﻿Imports System.Collections.Concurrent
Imports System.ComponentModel
Imports System.Collections.Specialized
Imports System.Text.RegularExpressions

Namespace Controls

    Friend Class ctlGacView

        <Serializable()>
        Private Structure GacInfo
            Public Property AssemblyName As String
            Public Property AssemblyVersion As Version
            Public Property Culture As String
            Public Property PublicKeyToken As String
            Public Property ProcessorArchitecture As String
            Public Property FileVersion As Version
            Public Property ModifiedDate As DateTime
            Public Property FileLocation As String
            Public Property FileSize As Integer
            Public Property FileDescription As String
            Public Property Company As String
            Public Property ProductName As String
        End Structure

        Private Structure AssemblyDirectoryInfo
            Public Property AssemblyVersion As Version
            Public Property Culture As String
            Public Property PublicKeyToken As String
        End Structure

        Private Shared Function CreateDataTable() As DataTable
            Dim oDtResults As DataTable = Nothing
            Try
                oDtResults = New DataTable
                oDtResults.Columns.Add(Constants.GacView.GacViewDataTableColumns.SELECTED, GetType(Boolean))
                oDtResults.Columns.Add(Constants.GacView.GacViewDataTableColumns.ASSEMBLY_NAME, GetType(String))
                oDtResults.Columns.Add(Constants.GacView.GacViewDataTableColumns.VERSION, GetType(Version))
                oDtResults.Columns.Add(Constants.GacView.GacViewDataTableColumns.CULTURE, GetType(String))
                oDtResults.Columns.Add(Constants.GacView.GacViewDataTableColumns.PROCESSOR_ARCHITECTURE, GetType(String))
                oDtResults.Columns.Add(Constants.GacView.GacViewDataTableColumns.PUBLIC_TOKEN, GetType(String))
                oDtResults.Columns.Add(Constants.GacView.GacViewDataTableColumns.FILE_VERSION, GetType(Version))
                oDtResults.Columns.Add(Constants.GacView.GacViewDataTableColumns.MODIFIED_DATE, GetType(DateTime))
                oDtResults.Columns.Add(Constants.GacView.GacViewDataTableColumns.FILE_LOCATION, GetType(String))
                oDtResults.Columns.Add(Constants.GacView.GacViewDataTableColumns.FILE_SIZE, GetType(Integer))
                oDtResults.Columns.Add(Constants.GacView.GacViewDataTableColumns.FILE_DESCRIPTION, GetType(String))
                oDtResults.Columns.Add(Constants.GacView.GacViewDataTableColumns.FILE_COMPANY, GetType(String))
                oDtResults.Columns.Add(Constants.GacView.GacViewDataTableColumns.FILE_PRODUCT_NAME, GetType(String))

                Return oDtResults
            Catch oExp As Exception
                If (oDtResults IsNot Nothing) Then
                    Try
                    Catch
                        oDtResults.Dispose()
                    End Try
                End If
                Throw
            End Try
        End Function

        Private Sub ctlGacView_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Try
                cboFrameworkVersion.SelectedIndex = cboFrameworkVersion.Items.Count - 1
                btnLoad.Focus()
            Catch oExp As Exception
                Try
                    If (Not Visible) Then Show()
                Catch
                End Try
                Helper.DisplayException(oExp, Me, "Exception Loading Gac View Control")
            End Try
        End Sub

        Private Sub btnLoad_Click(sender As Object, e As EventArgs) Handles btnLoad.Click
            Dim oCursor As Cursor = Cursor.Current
            Try
                Cursor.Current = Cursors.WaitCursor
                If (m_oBindingSourceGacList.DataSource IsNot Nothing) Then
                    Dim oDtPrevious As DataTable = TryCast(m_oBindingSourceGacList.DataSource, DataTable)
                    If (oDtPrevious IsNot Nothing) Then oDtPrevious.Dispose()
                    m_oBindingSourceGacList.Filter = String.Empty
                    m_oBindingSourceGacList.Sort = String.Empty
                    m_oBindingSourceGacList.DataSource = Nothing
                End If

                Dim szGacPath As String = GetGacPath()

                If (String.IsNullOrWhiteSpace(szGacPath)) Then Return
                If (Not Directory.Exists(szGacPath)) Then
                    Helper.DisplayMessage(Me, "Unable to find path " & szGacPath, "Unable To Load")
                    Return
                End If

                Dim clcPaths As List(Of String) = New List(Of String) From {Path.Combine(szGacPath, "GAC"), Path.Combine(szGacPath, "GAC_32"), Path.Combine(szGacPath, "GAC_64"), Path.Combine(szGacPath, "GAC_MSIL")}

                Dim clcResults As ConcurrentBag(Of GacInfo) = New ConcurrentBag(Of GacInfo)()

                For Each szRootPath As String In clcPaths
                    If (Not Directory.Exists(szRootPath)) Then Continue For
                    Dim oRootPath As DirectoryInfo = New DirectoryInfo(szRootPath)
                    Parallel.ForEach(oRootPath.GetDirectories(), Sub(oAssemblyRoot As DirectoryInfo)
                                                                     For Each oDirInfo As DirectoryInfo In oAssemblyRoot.GetDirectories()
                                                                         ' Find any DLL or Exe
                                                                         Dim oAssemblyInfo As FileInfo = oDirInfo.GetFiles("*.*", SearchOption.TopDirectoryOnly).Where(Function(oFileInfo As FileInfo) As Boolean
                                                                                                                                                                           Return (oFileInfo.Extension.Equals(".dll", StringComparison.OrdinalIgnoreCase)) OrElse (oFileInfo.Extension.Equals(".exe", StringComparison.OrdinalIgnoreCase))
                                                                                                                                                                       End Function).FirstOrDefault()
                                                                         If (Not oAssemblyInfo.Exists) Then Return

                                                                         Dim oAssemblyDirectoryInfo As AssemblyDirectoryInfo = GetAssemblyDirectoryInfo(oAssemblyInfo)

                                                                         Dim oGacInfo As GacInfo = New GacInfo()
                                                                         oGacInfo.AssemblyName = oAssemblyInfo.Name
                                                                         oGacInfo.AssemblyVersion = oAssemblyDirectoryInfo.AssemblyVersion
                                                                         oGacInfo.Culture = oAssemblyDirectoryInfo.Culture
                                                                         oGacInfo.FileLocation = oAssemblyInfo.Directory.FullName
                                                                         oGacInfo.FileSize = oAssemblyInfo.Length

                                                                         Try
                                                                             Dim oFileVersion As FileVersionInfo = FileVersionInfo.GetVersionInfo(oAssemblyInfo.FullName)
                                                                             oGacInfo.FileVersion = New Version(oFileVersion.FileMajorPart, oFileVersion.FileMinorPart, oFileVersion.FileBuildPart, oFileVersion.FilePrivatePart)
                                                                             oGacInfo.FileDescription = oFileVersion.FileDescription
                                                                             oGacInfo.Company = oFileVersion.CompanyName
                                                                             oGacInfo.ProductName = oFileVersion.ProductName
                                                                         Catch
                                                                         End Try
                                                                         oGacInfo.ModifiedDate = oAssemblyInfo.LastWriteTime
                                                                         oGacInfo.ProcessorArchitecture = DetermineProcessorArchitecture(oAssemblyInfo)
                                                                         oGacInfo.PublicKeyToken = oAssemblyDirectoryInfo.PublicKeyToken

                                                                         clcResults.Add(oGacInfo)
                                                                     Next
                                                                 End Sub)
                Next

                Dim oDtResults As DataTable = CreateDataTable()
                For Each oGacInfo As GacInfo In clcResults
                    Dim oDrData As DataRow = oDtResults.NewRow()
                    oDrData(Constants.GacView.GacViewDataTableColumns.SELECTED) = False
                    oDrData(Constants.GacView.GacViewDataTableColumns.ASSEMBLY_NAME) = oGacInfo.AssemblyName
                    oDrData(Constants.GacView.GacViewDataTableColumns.VERSION) = oGacInfo.AssemblyVersion
                    oDrData(Constants.GacView.GacViewDataTableColumns.FILE_VERSION) = oGacInfo.FileVersion
                    oDrData(Constants.GacView.GacViewDataTableColumns.CULTURE) = oGacInfo.Culture
                    oDrData(Constants.GacView.GacViewDataTableColumns.FILE_LOCATION) = oGacInfo.FileLocation
                    oDrData(Constants.GacView.GacViewDataTableColumns.MODIFIED_DATE) = oGacInfo.ModifiedDate
                    oDrData(Constants.GacView.GacViewDataTableColumns.PROCESSOR_ARCHITECTURE) = oGacInfo.ProcessorArchitecture
                    oDrData(Constants.GacView.GacViewDataTableColumns.PUBLIC_TOKEN) = oGacInfo.PublicKeyToken
                    oDrData(Constants.GacView.GacViewDataTableColumns.FILE_SIZE) = oGacInfo.FileSize
                    oDrData(Constants.GacView.GacViewDataTableColumns.FILE_DESCRIPTION) = oGacInfo.FileDescription
                    oDrData(Constants.GacView.GacViewDataTableColumns.FILE_COMPANY) = oGacInfo.Company
                    oDrData(Constants.GacView.GacViewDataTableColumns.FILE_PRODUCT_NAME) = oGacInfo.ProductName
                    oDtResults.Rows.Add(oDrData)
                Next

                oDtResults.AcceptChanges()
                m_oBindingSourceGacList.DataSource = oDtResults
                m_oBindingSourceGacList.Sort = Constants.GacView.GacViewDataTableColumns.ASSEMBLY_NAME

                grdGacList.AutoResizeColumns()
            Catch oExp As Exception
                Helper.DisplayException(oExp, Me, "Exception Loading Gac List")
            Finally
                Cursor.Current = oCursor
            End Try
        End Sub

        Private Function GetGacPath() As String
            Dim szWindowsFolder As String = Environment.GetFolderPath(Environment.SpecialFolder.Windows)

            If (cboFrameworkVersion.SelectedItem.IndexOf("3.5", StringComparison.OrdinalIgnoreCase) >= 0) Then
                Return Path.Combine(szWindowsFolder, "assembly")
            ElseIf (cboFrameworkVersion.SelectedItem.IndexOf("4.X", StringComparison.OrdinalIgnoreCase) >= 0) Then
                Return Path.Combine(szWindowsFolder, "Microsoft.NET\assembly")
            Else
                Helper.DisplayMessage(Me, "The currently selected framework version is not supported.", "Unable To Load Assemblies")
                Return String.Empty
            End If
        End Function

        Private Shared Function GetAssemblyDirectoryInfo(ByVal assemblyFileInfo As FileInfo) As AssemblyDirectoryInfo
            Dim aszParentFolderInfo As String() = assemblyFileInfo.Directory.Name.Split("_".ToCharArray(), StringSplitOptions.None)

            Dim oAssemblyDirectoryInfo As AssemblyDirectoryInfo = New AssemblyDirectoryInfo()
            Select Case aszParentFolderInfo.Length
                Case 2
                    Dim oVersion As Version = Nothing
                    If (Version.TryParse(aszParentFolderInfo(0), oVersion)) Then
                        oAssemblyDirectoryInfo.AssemblyVersion = oVersion
                    End If

                    oAssemblyDirectoryInfo.PublicKeyToken = aszParentFolderInfo(1)
                Case 3
                    Dim oVersion As Version = Nothing
                    If (Version.TryParse(aszParentFolderInfo(0), oVersion)) Then
                        oAssemblyDirectoryInfo.AssemblyVersion = oVersion
                    End If
                    oAssemblyDirectoryInfo.Culture = aszParentFolderInfo(1)
                    oAssemblyDirectoryInfo.PublicKeyToken = aszParentFolderInfo(2)
                Case 4
                    Dim oVersion As Version = Nothing
                    If (Version.TryParse(aszParentFolderInfo(1), oVersion)) Then
                        oAssemblyDirectoryInfo.AssemblyVersion = oVersion
                    End If
                    oAssemblyDirectoryInfo.Culture = aszParentFolderInfo(2)
                    oAssemblyDirectoryInfo.PublicKeyToken = aszParentFolderInfo(3)
                Case Else
                    Return Nothing
            End Select

            Return oAssemblyDirectoryInfo
        End Function

        Private Shared Function DetermineProcessorArchitecture(ByVal assemblyFileInfo As FileInfo) As String
            Dim szDirectoryType As String = assemblyFileInfo.Directory.Parent.Parent.Name.ToUpper()

            Select Case szDirectoryType
                Case "GAC_MSIL"
                    Return "MSIL"
                Case "GAC_32"
                    Return "x86"
                Case "GAC_64"
                    Return "x64"
                Case Else
                    Return String.Empty
            End Select

        End Function

        Private Sub m_oBindingSourceGacList_ListChanged(sender As Object, e As ListChangedEventArgs) Handles m_oBindingSourceGacList.ListChanged
            Try
                grpAssemblies.Text = String.Format("Showing {0} Assemblies", m_oBindingSourceGacList.Count)

                btnCheckAssemblies.Enabled = m_oBindingSourceGacList.Count > 0
                btnUncheckAssemblies.Enabled = m_oBindingSourceGacList.Count > 0
            Catch
            End Try
        End Sub

        Private Sub txtCandidateFilter_KeyUp(sender As Object, e As KeyEventArgs) Handles txtCandidateFilter.KeyUp
            Try
                If (e.KeyCode = Keys.Escape) Then
                    txtCandidateFilter.Text = String.Empty
                    m_oBindingSourceGacList.Filter = String.Empty
                    e.Handled = True
                    Return
                End If

                If (txtCandidateFilter.Text.Length < 3) Then
                    m_oBindingSourceGacList.Filter = String.Empty
                    Return
                End If

                Dim szFilter As String =
                        String.Format("[{1}] like '%{0}%' OR [{2}] like '%{0}%' OR Convert([{3}], 'System.String') like '%{0}%' OR Convert([{4}], 'System.String') like '%{0}%' OR [{5}] like '%{0}%' OR [{6}] like '%{0}%' OR Convert([{7}], 'System.String') like '%{0}%' OR [{8}] like '%{0}%' OR [{9}] like '%{0}%' OR [{10}] like '%{0}%' OR [{11}] like '%{0}%' OR Convert([{12}], 'System.String') like '%{0}%'",
                                      txtCandidateFilter.Text, Constants.GacView.GacViewDataTableColumns.ASSEMBLY_NAME, Constants.GacView.GacViewDataTableColumns.PUBLIC_TOKEN,
                                      Constants.GacView.GacViewDataTableColumns.VERSION, Constants.GacView.GacViewDataTableColumns.FILE_VERSION, Constants.GacView.GacViewDataTableColumns.CULTURE,
                                      Constants.GacView.GacViewDataTableColumns.PROCESSOR_ARCHITECTURE, Constants.GacView.GacViewDataTableColumns.MODIFIED_DATE, Constants.GacView.GacViewDataTableColumns.FILE_LOCATION, Constants.GacView.GacViewDataTableColumns.FILE_DESCRIPTION, Constants.GacView.GacViewDataTableColumns.FILE_COMPANY, Constants.GacView.GacViewDataTableColumns.FILE_PRODUCT_NAME, Constants.GacView.GacViewDataTableColumns.FILE_SIZE)
                m_oBindingSourceGacList.Filter = szFilter
            Catch
            End Try
        End Sub

        Private Sub grdGacList_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles grdGacList.CellContentClick
            Try
                Dim oColumn As DataGridViewColumn = grdGacList.Columns()(e.ColumnIndex)
                If oColumn.GetType() <> GetType(DataGridViewLinkColumn) Then
                    Return
                End If

                Dim szUrl As String = DirectCast(grdGacList.Rows()(e.RowIndex).DataBoundItem, DataRowView).Row()(Constants.GacView.GacViewDataTableColumns.FILE_LOCATION).ToString()
                Process.Start(szUrl)
            Catch
            End Try
        End Sub

        Private Sub ToggleCheckedAssemblies(sender As Object, e As EventArgs) Handles btnCheckAssemblies.Click, btnUncheckAssemblies.Click
            Dim oCursor As Cursor = Cursor.Current
            Try
                Cursor.Current = Cursors.WaitCursor
                Dim bSelected As Boolean = btnCheckAssemblies.Equals(sender)
                m_oBindingSourceGacList.SuspendBinding()

                ' Remove the handler to keep the event from being fired every time we set the flag
                RemoveHandler m_oBindingSourceGacList.ListChanged, AddressOf m_oBindingSourceGacList_ListChanged

                For iIndex As Integer = 0 To grdGacList.Rows.Count - 1
                    Dim oRow As DataGridViewRow = grdGacList.Rows(iIndex)
                    If (Not oRow.Visible) Then Continue For

                    Dim oDrData As DataRow = DirectCast(oRow.DataBoundItem, DataRowView).Row()
                    oDrData(Constants.GacView.GacViewDataTableColumns.SELECTED) = bSelected
                Next
            Catch oExp As Exception
                Helper.DisplayException(oExp, Me, "Exception Selecting/Unselecting Assemblies")
            Finally
                Cursor.Current = oCursor
                m_oBindingSourceGacList.ResumeBinding()
                AddHandler m_oBindingSourceGacList.ListChanged, AddressOf m_oBindingSourceGacList_ListChanged
            End Try
        End Sub

        Private Sub ContextMenuStripGacList_Opening(sender As Object, e As CancelEventArgs) Handles ContextMenuStripGacList.Opening
            Try
                grdGacList.EndEdit()
                Dim iValue As Integer = m_oBindingSourceGacList.Find("Selected", True)
                CopySelectedToToolStripMenuItem.Enabled = (iValue >= 0)
                AllSelectedClipboardToolStripMenuItem.Enabled = (iValue >= 0)
                AllSelectedFolderToolStripMenuItem.Enabled = (iValue >= 0)
                UninstallAllSelectedToolStripMenuItem.Enabled = (iValue >= 0)

                CopyCurrentItemToToolStripMenuItem.Text = "Current Item To"
                Dim oDrCurrent As DataRow = DirectCast(grdGacList.CurrentRow().DataBoundItem(), DataRowView).Row()
                CopyCurrentItemToToolStripMenuItem.Text &= " (" & oDrCurrent(Constants.GacView.GacViewDataTableColumns.ASSEMBLY_NAME) & ")"
            Catch
            End Try
        End Sub

        Private Sub CurrentItemClipboardToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles CurrentItemClipboardToolStripMenuItem.Click
            Try
                Dim oDrCurrent As DataRow = DirectCast(grdGacList.CurrentRow.DataBoundItem, DataRowView).Row()

                Dim szName As String = oDrCurrent(Constants.GacView.GacViewDataTableColumns.ASSEMBLY_NAME)
                Dim szSource As String = Path.Combine(oDrCurrent(Constants.GacView.GacViewDataTableColumns.FILE_LOCATION), szName)

                Dim clcToClipboard As StringCollection = New StringCollection()
                clcToClipboard.Add(szSource)

                Clipboard.Clear()
                Clipboard.SetFileDropList(clcToClipboard)

                Helper.DisplayMessage(Me, "Current file successfully copied to clipboard.", "File Copied")
            Catch oExp As Exception
                Helper.DisplayException(oExp, Me, "Unable to copy item to clipboard.")
            End Try
        End Sub

        Private Sub CurrentItemFolderToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles CurrentItemFolderToolStripMenuItem.Click
            Try
                If (FolderBrowserDialogGacView.ShowDialog(Parent) <> DialogResult.OK) Then Return

                Dim szFolder As String = FolderBrowserDialogGacView.SelectedPath
                Dim oDrCurrent As DataRow = DirectCast(grdGacList.CurrentRow.DataBoundItem, DataRowView).Row()

                Dim szName As String = oDrCurrent(Constants.GacView.GacViewDataTableColumns.ASSEMBLY_NAME)
                Dim szSource As String = Path.Combine(oDrCurrent(Constants.GacView.GacViewDataTableColumns.FILE_LOCATION), szName)
                Dim szDestination As String = Path.Combine(szFolder, szName)
                File.Copy(szSource, szDestination)

                Helper.DisplayMessage(Me, "Current file successfully copied.", "File Copied")
            Catch oExp As Exception
                Helper.DisplayException(oExp, Me, "Unable to copy item to destination folder.")
            End Try
        End Sub

        Private Function GetSelectedFiles() As Dictionary(Of String, String)
            Dim clcFiles As Dictionary(Of String, String) = New Dictionary(Of String, String)()

            grdGacList.EndEdit()
            m_oBindingSourceGacList.EndEdit()
            Dim oDtSource As DataTable = DirectCast(m_oBindingSourceGacList.DataSource, DataTable)
            For Each oDrFile As DataRow In oDtSource.Select(Constants.GacView.GacViewDataTableColumns.SELECTED & "=true")
                Dim szName As String = oDrFile(Constants.GacView.GacViewDataTableColumns.ASSEMBLY_NAME)
                Dim szFile As String = Path.Combine(oDrFile(Constants.GacView.GacViewDataTableColumns.FILE_LOCATION), szName)
                If (clcFiles.ContainsKey(szName)) Then
                    Helper.DisplayMessage(Me, "You have two files with the name " & szName & " selected.  Only the first one will be processed.", "Duplicate File Selected")
                    Continue For
                End If
                clcFiles.Add(szName, szFile)
            Next
            Return clcFiles
        End Function

        Private Sub UninstallCurrentItemToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles UninstallCurrentItemToolStripMenuItem.Click
            Try
                grdGacList.EndEdit()
                m_oBindingSourceGacList.EndEdit()

                Dim oDrCurrent As DataRow = DirectCast(grdGacList.CurrentRow.DataBoundItem, DataRowView).Row()

                UninstallAssemblies(oDrCurrent)
                btnLoad_Click(sender, e)
            Catch oExp As Exception
                Helper.DisplayException(oExp, Me, "Unable to uninstall current assembly.")
            End Try
        End Sub

        Private Sub UninstallAllSelectedToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles UninstallAllSelectedToolStripMenuItem.Click
            Try
                grdGacList.EndEdit()
                m_oBindingSourceGacList.EndEdit()

                Dim oDtSource As DataTable = DirectCast(m_oBindingSourceGacList.DataSource, DataTable)
                UninstallAssemblies(oDtSource.Select(Constants.GacView.GacViewDataTableColumns.SELECTED & "=true"))
                btnLoad_Click(sender, e)
            Catch oExp As Exception
                Helper.DisplayException(oExp, Me, "Unable to uninstall all selected assemblies.")
            End Try
        End Sub

        Private Sub UninstallAssemblies(ByVal ParamArray filesToUninstall As DataRow())
            Dim szSdkPath As String = String.Empty
            Dim szGacUtilPath As String = String.Empty
            If Not GetPathToGacUtil(szSdkPath, szGacUtilPath) Then Return

            ' Check first for any assemblies that are on the banned list
            For Each oDrCurrent As DataRow In filesToUninstall
                Dim szPublicToken As String = oDrCurrent(Constants.GacView.GacViewDataTableColumns.PUBLIC_TOKEN).ToString().ToLower()
                Dim szName As String = oDrCurrent(Constants.GacView.GacViewDataTableColumns.ASSEMBLY_NAME)
                Dim szSource As String = Path.Combine(oDrCurrent(Constants.GacView.GacViewDataTableColumns.FILE_LOCATION), szName)

                Select Case szPublicToken
                    Case "b77a5c561934e089", "b03f5f7f11d50a3a", "89845dcd8080cc91", "71e9bce111e9429c", "31bf3856ad364e35"
                        Helper.DisplayMessage(Me, String.Format("You are not allowed to remove assembly '{0}'.  If are sure you want to do this, you will need to do it manually.", szSource), "Assembly Restricted For Uninstall")
                        Return
                End Select
            Next

            ' Now process each file
            For Each oDrCurrent As DataRow In filesToUninstall
                Dim szName As String = Path.GetFileNameWithoutExtension(oDrCurrent(Constants.GacView.GacViewDataTableColumns.ASSEMBLY_NAME))
                Dim szVersion As String = oDrCurrent(Constants.GacView.GacViewDataTableColumns.VERSION).ToString()
                Dim szCulture As String = oDrCurrent(Constants.GacView.GacViewDataTableColumns.CULTURE).ToString()
                Dim szPublicToken As String = oDrCurrent(Constants.GacView.GacViewDataTableColumns.PUBLIC_TOKEN).ToString()

                If (String.IsNullOrWhiteSpace(szCulture)) Then szCulture = "neutral"

                Dim szFullyQualifiedName As String = String.Format("""{0},Version={1}, Culture={2}, PublicKeyToken={3}""", szName, szVersion, szCulture, szPublicToken)
                Dim iCount As Integer = GetGlobalAssemblyCacheCount(szSdkPath, szGacUtilPath, szFullyQualifiedName)

                If (iCount <> 1) Then
                    Helper.DisplayMessage(Me, String.Format("Unable to process assembly '{0}'.  More than one assembly would be removed.  Please remove the assembly manually.", szFullyQualifiedName), "Unable To Process Assembly")
                    Return
                End If

                UninstallFromGlobalAssemblyCache(szSdkPath, szGacUtilPath, szFullyQualifiedName)
            Next
        End Sub

        Private Sub AllSelectedClipboardToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles AllSelectedClipboardToolStripMenuItem.Click
            Try
                Dim clcFiles As Dictionary(Of String, String) = GetSelectedFiles()
                Clipboard.Clear()

                Dim clcToClipboard As StringCollection = New StringCollection()
                clcToClipboard.AddRange(clcFiles.Values.ToArray())

                Clipboard.SetFileDropList(clcToClipboard)

                Helper.DisplayMessage(Me, String.Format("{0} file(s) successfully copied to clipboard.", clcFiles.Count), "Files Copied")
            Catch oExp As Exception
                Helper.DisplayException(oExp, Me, "Unable to copy item(s) to clipboard.")
            End Try
        End Sub

        Private Sub AllSelectedFolderToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles AllSelectedFolderToolStripMenuItem.Click
            Try
                If (FolderBrowserDialogGacView.ShowDialog(Parent) <> DialogResult.OK) Then Return

                Dim szFolder As String = FolderBrowserDialogGacView.SelectedPath
                Dim clcFiles As Dictionary(Of String, String) = GetSelectedFiles()

                For Each oKeyPair As KeyValuePair(Of String, String) In clcFiles
                    Dim szName As String = oKeyPair.Key
                    Dim szDestination As String = Path.Combine(szFolder, szName)
                    File.Copy(oKeyPair.Value, szDestination)
                Next

                Helper.DisplayMessage(Me, String.Format("{0} file(s) successfully copied.", clcFiles.Count), "Files Copied")
            Catch oExp As Exception
                Helper.DisplayException(oExp, Me, "Unable to copy item(s) to destination folder.")
            End Try
        End Sub

        Private Function GetPathToGacUtil(ByRef sdkPath As String, ByRef gacUtilPath As String) As Boolean
            If (cboFrameworkVersion.SelectedItem.IndexOf("3.5", StringComparison.OrdinalIgnoreCase) >= 0) Then
                sdkPath = Helper.GetFrameworkSDKPath(Helper.FrameworkSdkVersion.Framework35)
            ElseIf (cboFrameworkVersion.SelectedItem.IndexOf("4.X", StringComparison.OrdinalIgnoreCase) >= 0) Then
                sdkPath = Helper.GetFrameworkSDKPath(Helper.FrameworkSdkVersion.Framework40)
            End If

            If (String.IsNullOrWhiteSpace(sdkPath)) Then
                Helper.DisplayMessage(Me, "The currently selected framework version is not supported.", "Unable To Install Assemblies")
                Return False
            End If

            gacUtilPath = Path.Combine(sdkPath, "gacutil.exe")

            If (Not File.Exists(gacUtilPath)) Then
                Helper.DisplayMessage(Me, "Unable to locate gacutil.exe in the following directory: " & sdkPath, "Unable To Install Assemblies")
                Return False
            End If

            Return True
        End Function

        Private Sub InstallToGacToolStripMenuItem_Click(sender As System.Object, e As EventArgs) Handles InstallToGacToolStripMenuItem.Click
            Try
                Dim szSdkPath As String = String.Empty
                Dim szGacUtilPath As String = String.Empty
                If Not GetPathToGacUtil(szSdkPath, szGacUtilPath) Then Return

                If (m_oOpenFileDialog.ShowDialog(ParentForm) <> DialogResult.OK) Then Return

                For Each szFilePath As String In m_oOpenFileDialog.FileNames
                    Using oProcess As Process = New Process()
                        Dim oProcessStartInfo As New ProcessStartInfo(szGacUtilPath)
                        oProcessStartInfo.CreateNoWindow = True
                        oProcessStartInfo.UseShellExecute = False
                        oProcessStartInfo.Arguments = "/i " & Helper.ShortPathName(szFilePath)
                        oProcessStartInfo.ErrorDialog = False
                        oProcessStartInfo.RedirectStandardOutput = True
                        oProcessStartInfo.RedirectStandardError = True
                        oProcessStartInfo.RedirectStandardInput = False
                        oProcessStartInfo.WorkingDirectory = Path.GetDirectoryName(szSdkPath)
                        oProcess.StartInfo = oProcessStartInfo
                        oProcess.Start()

                        ' Do not wait for the child process to exit before reading to the end of its redirected stream.
                        ' Read the output stream first and then wait. This avoids a deadlock condition. 
                        Dim szStandardOutput As String = oProcess.StandardOutput.ReadToEnd()
                        Dim szStandardError As String = oProcess.StandardError.ReadToEnd()
                        oProcess.WaitForExit()
                        If oProcess.ExitCode <> 0 Then
                            Throw New ApplicationException(
                                    String.Format("Error returned from gacutil.exe. Exit Code: '{3}' Assembly: '{4}'{0}{0}Error text:{0}{1}{0}{0}Additional Text:{0}{2}",
                                                  Environment.NewLine, szStandardError, szStandardOutput, oProcess.ExitCode, szFilePath))
                        End If
                    End Using
                Next
                Helper.DisplayMessage(Me, String.Format("{0} file(s) successfully installed.", m_oOpenFileDialog.FileNames.Length), "Files Installed")
            Catch oExp As Exception
                Helper.DisplayException(oExp, Me, "Unable to install new assemblies to GAC.")
            Finally
                btnLoad_Click(sender, e)
            End Try
        End Sub

        Private Shared Function GetGlobalAssemblyCacheCount(ByVal sdkPath As String, ByVal gacUtilPath As String, ByVal fullyQualifiedName As String) As Integer
            Using oProcess As Process = New Process()
                Dim oProcessStartInfo As New ProcessStartInfo(gacUtilPath)
                oProcessStartInfo.CreateNoWindow = True
                oProcessStartInfo.UseShellExecute = False
                oProcessStartInfo.Arguments = "/l " & fullyQualifiedName
                oProcessStartInfo.ErrorDialog = False
                oProcessStartInfo.RedirectStandardOutput = True
                oProcessStartInfo.RedirectStandardError = True
                oProcessStartInfo.RedirectStandardInput = False
                oProcessStartInfo.WorkingDirectory = Path.GetDirectoryName(sdkPath)
                oProcess.StartInfo = oProcessStartInfo
                oProcess.Start()

                ' Do not wait for the child process to exit before reading to the end of its redirected stream.
                ' Read the output stream first and then wait. This avoids a deadlock condition. 
                Dim szStandardOutput As String = oProcess.StandardOutput.ReadToEnd()
                Dim szStandardError As String = oProcess.StandardError.ReadToEnd()
                oProcess.WaitForExit()
                If oProcess.ExitCode <> 0 Then
                    Throw New ApplicationException(
                            String.Format("Error returned from gacutil.exe. Exit Code: '{3}' Assembly: '{4}'{0}{0}Error text:{0}{1}{0}{0}Additional Text:{0}{2}",
                                          Environment.NewLine, szStandardError, szStandardOutput, oProcess.ExitCode, fullyQualifiedName))
                End If

                ' Parse out the result count
                Dim oMatch As Match = Regex.Match(szStandardOutput, "Number[\s]*of[\s]*items[\s]*=[\s]*(?<Count>[0-9]*)", RegexOptions.IgnoreCase Or RegexOptions.IgnorePatternWhitespace)
                If (Not oMatch.Success) Then Throw New ApplicationException(String.Format("Unable to determine impact count for assembly {1} from TF.exe output:{0}", szStandardOutput, fullyQualifiedName))

                Dim iCount As Integer
                If (Not Integer.TryParse(oMatch.Groups("Count").Value.Trim(), iCount)) Then Throw New ApplicationException(String.Format("Unable to determine impact count for assembly {1} from TF.exe output:{0}", szStandardOutput, fullyQualifiedName))

                Return iCount
            End Using
        End Function

        Private Shared Sub UninstallFromGlobalAssemblyCache(ByVal sdkPath As String, ByVal gacUtilPath As String, ByVal fullyQualifiedName As String)
            Using oProcess As Process = New Process()
                Dim oProcessStartInfo As New ProcessStartInfo(gacUtilPath)
                oProcessStartInfo.CreateNoWindow = True
                oProcessStartInfo.UseShellExecute = False
                oProcessStartInfo.Arguments = "/u " & fullyQualifiedName
                oProcessStartInfo.ErrorDialog = False
                oProcessStartInfo.RedirectStandardOutput = True
                oProcessStartInfo.RedirectStandardError = True
                oProcessStartInfo.RedirectStandardInput = False
                oProcessStartInfo.WorkingDirectory = Path.GetDirectoryName(sdkPath)
                oProcess.StartInfo = oProcessStartInfo
                oProcess.Start()

                ' Do not wait for the child process to exit before reading to the end of its redirected stream.
                ' Read the output stream first and then wait. This avoids a deadlock condition. 
                Dim szStandardOutput As String = oProcess.StandardOutput.ReadToEnd()
                Dim szStandardError As String = oProcess.StandardError.ReadToEnd()
                oProcess.WaitForExit()
                If oProcess.ExitCode <> 0 Then
                    Throw New ApplicationException(
                            String.Format("Error returned from gacutil.exe. Exit Code: '{3}' Assembly: '{4}'{0}{0}Error text:{0}{1}{0}{0}Additional Text:{0}{2}",
                                          Environment.NewLine, szStandardError, szStandardOutput, oProcess.ExitCode, fullyQualifiedName))
                End If

                ' Try to validate the return counts
                Dim oMatch As Match = Regex.Match(szStandardOutput, "Number[\s]*of[\s]*assemblies[\s]*uninstalled[\s]*=[\s]*(?<CountUninstalled>[0-9]*)[\s]*[\r\n]*Number[\s]*of[\s]*failures[\s]*=[\s]*]*(?<CountFailures>[0-9]*)", RegexOptions.IgnoreCase Or RegexOptions.IgnorePatternWhitespace)
                If (oMatch.Success) Then
                    Dim iCountUninstalled As Integer
                    Dim iCountFailures As Integer

                    If (Not Integer.TryParse(oMatch.Groups("CountUninstalled").Value.Trim(), iCountUninstalled)) Then Return
                    If (Not Integer.TryParse(oMatch.Groups("CountFailures").Value.Trim(), iCountFailures)) Then Return

                    If (iCountUninstalled = 0) OrElse (iCountFailures > 0) Then
                        Throw New ApplicationException(
                            String.Format("Assembly not properly uninstalled by gacutil.exe. Exit Code: '{3}' Assembly: '{4}'{0}{0}Error text:{0}{1}{0}{0}Additional Text:{0}{2}",
                                          Environment.NewLine, szStandardError, szStandardOutput, oProcess.ExitCode, fullyQualifiedName))
                    End If
                End If
            End Using
        End Sub
    End Class
End Namespace